In [1]:
# !pip install newspaper3k
In [2]:
# !pip install jpype1
In [3]:
# !pip install konlpy
In [4]:
# !pip install scikit-learn
In [5]:
# stop word 불러오기
f = open('ko_stop.txt','r',encoding='utf-8')
stopwords_ko = f.read()
f.close()
In [6]:
# stop word 리스트화
sw_ko = stopwords_ko.split()
In [7]:
from newspaper import Article
from konlpy.tag import Kkma
from konlpy.tag import Okt
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.preprocessing import normalize
import numpy as np
In [8]:
class SentenceTokenizer(object):
    def __init__(self):
        self.kkma = Kkma()
        self.twitter = Okt()
        self.stopwords = sw_ko
        #['중인' ,'만큼', '마찬가지', '꼬집었', "연합뉴스", "데일리", "동아일보", "중앙일보", "조선일보", "기자","아", "휴", "아이구", "아이쿠", "아이고", "어", "나", "우리", "저희", "따라", "의해", "을", "를", "에", "의", "가",]
    
    def url2sentences(self, url):
        article = Article(url, language='ko')
        article.download()
        article.parse()
        sentences = self.kkma.sentences(article.text)
        
        for idx in range(0, len(sentences)):
            if len(sentences[idx]) <= 10:
                sentences[idx-1] += (' ' + sentences[idx])
                sentences[idx] = ''
        
        return sentences

    def text2sentences(self, text):
        sentences = self.kkma.sentences(text)
        for idx in range(0, len(sentences)):
            if len(sentences[idx]) <= 10:
                sentences[idx-1] += (' ' + sentences[idx])
                sentences[idx] = ''

        return sentences

    def get_nouns(self, sentences):
        nouns = []
        for sentence in sentences:
            if sentence is not '':
                nouns.append(' '.join([noun for noun in self.twitter.nouns(str(sentence))
                                       if noun not in self.stopwords and len(noun) > 1]))

        return nouns
In [9]:
class GraphMatrix(object):

    def __init__(self):
        self.tfidf = TfidfVectorizer()
        self.cnt_vec = CountVectorizer()
        self.graph_sentence = []

    def build_sent_graph(self, sentence):
        tfidf_mat = self.tfidf.fit_transform(sentence).toarray()
        self.graph_sentence = np.dot(tfidf_mat, tfidf_mat.T)
        return self.graph_sentence

    def build_words_graph(self, sentence):
        cnt_vec_mat = normalize(self.cnt_vec.fit_transform(sentence).toarray().astype(float), axis=0)
        vocab = self.cnt_vec.vocabulary_
        return np.dot(cnt_vec_mat.T, cnt_vec_mat), {vocab[word] : word for word in vocab}
In [10]:
class Rank(object):

    def get_ranks(self, graph, d=0.85): # d = damping factor
        A = graph
        matrix_size = A.shape[0]
        for id in range(matrix_size):
            A[id, id] = 0 # diagonal 부분을 0으로
            link_sum = np.sum(A[:,id]) # A[:, id] = A[:][id]
            if link_sum != 0:
                A[:, id] /= link_sum
            A[:, id] *= -d
            A[id, id] = 1

        B = (1-d) * np.ones((matrix_size, 1))
        ranks = np.linalg.solve(A, B) # 연립방정식 Ax = b
        return {idx: r[0] for idx, r in enumerate(ranks)}
In [11]:
class TextRank(object):

    def __init__(self, text):
        self.sent_tokenize = SentenceTokenizer()
        
        if text[:5] in ('http:', 'https'):
            self.sentences = self.sent_tokenize.url2sentences(text)
        else:
            self.sentences = self.sent_tokenize.text2sentences(text)
        
        self.nouns = self.sent_tokenize.get_nouns(self.sentences)
        
        self.graph_matrix = GraphMatrix()
        self.sent_graph = self.graph_matrix.build_sent_graph(self.nouns)
        self.words_graph, self.idx2word = self.graph_matrix.build_words_graph(self.nouns)

        self.rank = Rank()
        self.sent_rank_idx = self.rank.get_ranks(self.sent_graph)
        self.sorted_sent_rank_idx = sorted(self.sent_rank_idx, key=lambda k: self.sent_rank_idx[k], reverse=True)

        self.word_rank_idx = self.rank.get_ranks(self.words_graph)
        self.sorted_word_rank_idx = sorted(self.word_rank_idx, key=lambda k: self.word_rank_idx[k], reverse=True)

    def summarize(self, sent_num=3):
        summary = []
        index=[]
        for idx in self.sorted_sent_rank_idx[:sent_num]:
            index.append(idx)

        index.sort()
        for idx in index:
            summary.append(self.sentences[idx])

        return summary

    def keywords(self, word_num=10):
        rank = Rank()
        rank_idx = rank.get_ranks(self.words_graph)
        sorted_rank_idx = sorted(rank_idx, key=lambda k: rank_idx[k], reverse=True)

        keywords = []

        index=[]
        for idx in sorted_rank_idx[:word_num]:
            index.append(idx)

        #index.sort()
        for idx in index:
            keywords.append(self.idx2word[idx])

        return keywords
In [12]:
# 웹에서 가져온 기사를 분석
url = 'https://www.edaily.co.kr/news/read?newsId=01774486622584696&mediaCodeNo=257&OutLnkChk=Y'
textrank = TextRank(url)
for row in textrank.summarize(3):
    print(row)
    print()
print('keywords :',textrank.keywords())
C:\Users\BowlMin\Anaconda3\envs\py36\lib\site-packages\jpype\_core.py:210: UserWarning: 
-------------------------------------------------------------------------------
Deprecated: convertStrings was not specified when starting the JVM. The default
behavior in JPype will be False starting in JPype 0.8. The recommended setting
for new code is convertStrings=False.  The legacy value of True was assumed for
this session. If you are a user of an application that reported this warning,
please file a ticket with the developer.
-------------------------------------------------------------------------------

  """)
서울시는 6일 팬 임팩트 코리아와 ` 서울 특별시 청년 실업 해소를 위한 사회 성과 보상사업` 계약을 체결했다.

이번 SIB 사업을 통해 서울시는 시내에 거주하고 일자리가 없는 청년들에게 국내· 외 취업과 창업을 위한 프로그램을 3년 간 제공하게 되며 총 사업비 30억원 가운데 29억원을 민간 투자로 조달하게 된다.

팬 임팩트 코리아는 민간 투자자를 모집하고 이후 사업을 진행할 수행기관을 공모할 예정이다.

keywords : ['투자자', '사업', '사회', '성과', '투자', '서울시', '청년', '국내', '위해', '진행']
In [13]:
# 개인 텍스트 데이터 활용
text = '''
[곰 같은 사람]
저는 곰 같다는 소리를 자주 듣는 사람입니다. 외형적으로 닮은 부분도 있지만, 리더로서, 구성원으로서 언제나 묵묵하게 저 자신이 해야 할 일을 했기 때문입니다. '디지털 인문 예술 입문'이라는 수업에서 마지막 조별 프로젝트를 제출해야 하는 일이 있었습니다. 그 당시 조원들은 수업 시간에 잠깐 배운 파이썬 프로그램을 활용할 것을 제안했고, 교수님께서는 어려울 것이니 실패를 통해 배운 것들을 제출하라고 말씀하셨습니다. 교수님의 말씀에 조원들도 약한 마음을 가졌지만 제가 솔선수범하여 컴퓨터 공학을 전공하는 친구에게 배우고, 현재 개발자로 일하고 있는 사람들과 교수님들에게 질문하였습니다. 조원들은 끝까지 프로젝트에 매달리는 모습을 보고 조원들도 열심히 잘 따라와 주었고 그 모습이 자극제가 되어 더욱 열심히 하게 되었습니다. 결국 프로젝트에 성공하여 교수님께 인정받았습니다. 이런 모습이 리더와 구성원들에게 항상 듬직한 모습을 보였고, 그 덕분에 믿음직한 사람, 열심히 일하는 사람으로 평가받았습니다. 또한 끝까지 노력해서 실패할 것은 없다는 교훈을 얻었고, 그 생각을 제 인생의 좌우명으로 가지고 있습니다.

[첫 도전]
친구와 웹 데이터 크롤링 프로젝트를 했을 때가 가장 기억에 남습니다. 데이터 분석이라는 부분에 관심을 가지고 친구와 Python에 관해 공부하고 있을 때였습니다. 매주 목요일 친구와 카페에서 만나던 도중, 문득 카페 브랜드별로 어떤 이미지를 가졌는지 궁금했습니다. 그래서 인스타그램에서 해시태그를 가져오는 프로그램을 만들기 위해 낮에 만나서 밤에 들어가는 일은 다반사였고, 각자 집에서도 공부하여 오류를 해결하는 작업을 하였습니다. 결국 인스타그램 측에서 차단하여 카페 브랜드별로 6천 개가량의 데이터만 가져와서 분석할 수 있었습니다. 이런 상황을 겪으니 제가 문제에 닥쳤을 때 끝까지 해결하기 위해 밤낮으로 노력했던 것이 제가 열정과 끈기가 있는 사람으로 느껴졌습니다.

'''
textrank = TextRank(text)
for row in textrank.summarize(3):
    print(row)
    print()
print('keywords :',textrank.keywords())
교수님의 말씀에 조원들도 약한 마음을 가졌지만 제가 솔선수범하여 컴퓨터 공학을 전공하는 친구에게 배우고, 현재 개발자로 일하고 있는 사람들과 교수님들에게 질문하였습니다.

결국 프로젝트에 성공하여 교수님께 인정받았습니다.

이런 모습이 리더와 구성원들에게 항상 듬직한 모습을 보였고, 그 덕분에 믿음직한 사람, 열심히 일하는 사람으로 평가 받았습니다.

keywords : ['친구', '사람', '말씀', '프로그램', '조원', '위해', '해결', '교수', '데이터', '인스타']